
import aiomysql
import threading
from typing import Union, Optional
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine

import settings

class AttrDict(dict):
    def __getattr__(self, name):
        try:
            return self[name]
        except KeyError:
            return None


class AttrDictCursor(aiomysql.DictCursor):
    dict_type = AttrDict


class AioMysql:
    _instance_lock = threading.Lock()

    def __init__(self, **kwargs):
        self.pool = None
        self.host = kwargs.get("host") or settings.HOST
        self.port = kwargs.get("port") or settings.PORT
        self.username = kwargs.get("username") or settings.USERNAME
        self.password = kwargs.get("password") or settings.PASSWORD
        self.db_name = kwargs.get("database") or settings.DATABASE

    def __new__(cls, *args, **kwargs):
        if not hasattr(AioMysql, "_instance"):
            with AioMysql._instance_lock:
                if not hasattr(AioMysql, "_instance"):
                    AioMysql._instance = super(AioMysql, cls).__new__(cls, *args, **kwargs)
        return AioMysql._instance

    async def connect_pool(self):
        """设置mysql连接池"""
        print("连接数据库:", self.host)
        self.pool = await aiomysql.create_pool(
            host=self.host,
            port=self.port,
            user=self.username,
            password=self.password,
            db=self.db_name,
            autocommit=False
        )

    async def select(self, sql: str, many: bool = True) -> dict:
        """
        数据库选择操作
            :param sql: 待执行的SQL语句
            :param many: 是否返回所有数据，否则只返回第一条
        """
        if not self.pool: await self.connect_pool()
        async with self.pool.acquire() as conn:
            async with conn.cursor(AttrDictCursor) as cursor:
                await cursor.execute(sql)
                if many:
                    data = await cursor.fetchall()
                else:
                    data = await cursor.fetchone()
                return data

    async def commit(self, sql: Union[str, list]) -> bool:
        """
        数据库选择操作
            :param sql: 待执行的SQL语句
        """
        if not self.pool: await self.connect_pool()
        async with self.pool.acquire() as conn:
            async with conn.cursor() as cursor:
                if isinstance(sql, str):
                    sql = [sql, ]
                elif not isinstance(sql, list):
                    raise TypeError("sql 参数必须传入字符串或者列表！")
                for cause in sql:
                    await cursor.execute(cause)
                try:
                    await cursor.commit(cause)
                except:
                    await conn.rollback()
                    return False
                return True

    async def _close(self):
        """
        关闭连接池
        """
        await self.pool.wait_closed()


aioDb = AioMysql()